/*
 * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/SimpleHttpConnectionManager.java,v 1.23 2004/10/16 22:40:08 mbecke Exp $
 * $Revision: 480424 $
 * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
 *
 * ====================================================================
 *
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */

package org.apache.commons.httpclient;

import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
import org.apache.commons.logging.Log;

/**
 * A connection manager that provides access to a single HttpConnection. This
 * manager makes no attempt to provide exclusive access to the contained
 * HttpConnection.
 * 
 * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
 * @author Eric Johnson
 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
 * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
 * @author Laura Werner
 * 
 * @since 2.0
 */
public class SimpleHttpConnectionManager implements HttpConnectionManager {

	// private static final Log LOG =
	// LogFactory.getLog(SimpleHttpConnectionManager.class);

	private static final String MISUSE_MESSAGE = "SimpleHttpConnectionManager being used incorrectly.  Be sure that"
			+ " HttpMethod.releaseConnection() is always called and that only one thread"
			+ " and/or method is using this connection manager at a time.";

	/**
	 * Since the same connection is about to be reused, make sure the previous
	 * request was completely processed, and if not consume it now.
	 * 
	 * @param conn
	 *            The connection
	 */
	static void finishLastResponse(HttpConnection conn) {
		InputStream lastResponse = conn.getLastResponseInputStream();
		if (lastResponse != null) {
			conn.setLastResponseInputStream(null);
			try {
				lastResponse.close();
			} catch (IOException ioe) {
				conn.close();
			}
		}
	}

	/** The http connection */
	protected HttpConnection httpConnection;

	/**
	 * Collection of parameters associated with this connection manager.
	 */
	private HttpConnectionManagerParams params = new HttpConnectionManagerParams();

	/**
	 * The time the connection was made idle.
	 */
	private long idleStartTime = Long.MAX_VALUE;

	/**
	 * Used to test if {@link #httpConnection} is currently in use (i.e. checked
	 * out). This is only used as a sanity check to help debug cases where this
	 * connection manager is being used incorrectly. It will not be used to
	 * enforce thread safety.
	 */
	private volatile boolean inUse = false;

	private boolean alwaysClose = false;

	/**
	 * The connection manager created with this constructor will try to keep the
	 * connection open (alive) between consecutive requests if the alwaysClose
	 * parameter is set to <tt>false</tt>. Otherwise the connection manager will
	 * always close connections upon release.
	 * 
	 * @param alwaysClose
	 *            if set <tt>true</tt>, the connection manager will always close
	 *            connections upon release.
	 */
	public SimpleHttpConnectionManager(boolean alwaysClose) {
		super();
		this.alwaysClose = alwaysClose;
	}

	/**
	 * The connection manager created with this constructor will always try to
	 * keep the connection open (alive) between consecutive requests.
	 */
	public SimpleHttpConnectionManager() {
		super();
	}

	/**
	 * @see HttpConnectionManager#getConnection(HostConfiguration)
	 */
	public HttpConnection getConnection(HostConfiguration hostConfiguration) {
		return getConnection(hostConfiguration, 0);
	}

	/**
	 * Gets the staleCheckingEnabled value to be set on HttpConnections that are
	 * created.
	 * 
	 * @return <code>true</code> if stale checking will be enabled on
	 *         HttpConections
	 * 
	 * @see HttpConnection#isStaleCheckingEnabled()
	 * 
	 * @deprecated Use
	 *             {@link HttpConnectionManagerParams#isStaleCheckingEnabled()},
	 *             {@link HttpConnectionManager#getParams()}.
	 */
	public boolean isConnectionStaleCheckingEnabled() {
		return this.params.isStaleCheckingEnabled();
	}

	/**
	 * Sets the staleCheckingEnabled value to be set on HttpConnections that are
	 * created.
	 * 
	 * @param connectionStaleCheckingEnabled
	 *            <code>true</code> if stale checking will be enabled on
	 *            HttpConections
	 * 
	 * @see HttpConnection#setStaleCheckingEnabled(boolean)
	 * 
	 * @deprecated Use
	 *             {@link HttpConnectionManagerParams#setStaleCheckingEnabled(boolean)}
	 *             , {@link HttpConnectionManager#getParams()}.
	 */
	public void setConnectionStaleCheckingEnabled(
			boolean connectionStaleCheckingEnabled) {
		this.params.setStaleCheckingEnabled(connectionStaleCheckingEnabled);
	}

	/**
	 * This method always returns the same connection object. If the connection
	 * is already open, it will be closed and the new host configuration will be
	 * applied.
	 * 
	 * @param hostConfiguration
	 *            The host configuration specifying the connection details.
	 * @param timeout
	 *            this parameter has no effect. The connection is always
	 *            returned immediately.
	 * @since 3.0
	 */
	public HttpConnection getConnectionWithTimeout(
			HostConfiguration hostConfiguration, long timeout) {

		if (httpConnection == null) {
			httpConnection = new HttpConnection(hostConfiguration);
			httpConnection.setHttpConnectionManager(this);
			httpConnection.getParams().setDefaults(this.params);
		} else {

			// make sure the host and proxy are correct for this connection
			// close it and set the values if they are not
			if (!hostConfiguration.hostEquals(httpConnection)
					|| !hostConfiguration.proxyEquals(httpConnection)) {

				if (httpConnection.isOpen()) {
					httpConnection.close();
				}

				httpConnection.setHost(hostConfiguration.getHost());
				httpConnection.setPort(hostConfiguration.getPort());
				httpConnection.setProtocol(hostConfiguration.getProtocol());
				httpConnection.setLocalAddress(hostConfiguration
						.getLocalAddress());

				httpConnection.setProxyHost(hostConfiguration.getProxyHost());
				httpConnection.setProxyPort(hostConfiguration.getProxyPort());
			} else {
				finishLastResponse(httpConnection);
			}
		}

		// remove the connection from the timeout handler
		idleStartTime = Long.MAX_VALUE;

		if (inUse) // LOG.warn(MISUSE_MESSAGE);
			inUse = true;

		return httpConnection;
	}

	/**
	 * @see HttpConnectionManager#getConnection(HostConfiguration, long)
	 * 
	 * @deprecated Use #getConnectionWithTimeout(HostConfiguration, long)
	 */
	public HttpConnection getConnection(HostConfiguration hostConfiguration,
			long timeout) {
		return getConnectionWithTimeout(hostConfiguration, timeout);
	}

	/**
	 * @see HttpConnectionManager#releaseConnection(org.apache.commons.httpclient.HttpConnection)
	 */
	public void releaseConnection(HttpConnection conn) {
		if (conn != httpConnection) {
			throw new IllegalStateException(
					"Unexpected release of an unknown connection.");
		}
		if (this.alwaysClose) {
			httpConnection.close();
		} else {
			// make sure the connection is reuseable
			finishLastResponse(httpConnection);
		}

		inUse = false;

		// track the time the connection was made idle
		idleStartTime = System.currentTimeMillis();
	}

	/**
	 * Returns {@link HttpConnectionManagerParams parameters} associated with
	 * this connection manager.
	 * 
	 * @since 2.1
	 * 
	 * @see HttpConnectionManagerParams
	 */
	public HttpConnectionManagerParams getParams() {
		return this.params;
	}

	/**
	 * Assigns {@link HttpConnectionManagerParams parameters} for this
	 * connection manager.
	 * 
	 * @since 2.1
	 * 
	 * @see HttpConnectionManagerParams
	 */
	public void setParams(final HttpConnectionManagerParams params) {
		if (params == null) {
			throw new IllegalArgumentException("Parameters may not be null");
		}
		this.params = params;
	}

	/**
	 * @since 3.0
	 */
	public void closeIdleConnections(long idleTimeout) {
		long maxIdleTime = System.currentTimeMillis() - idleTimeout;
		if (idleStartTime <= maxIdleTime) {
			httpConnection.close();
		}
	}

	/**
	 * since 3.1
	 */
	public void shutdown() {
		httpConnection.close();
	}

}
